home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
SGI Developer Toolbox 6.1
/
SGI Developer Toolbox 6.1 - Disc 4.iso
/
src
/
haeberli
/
libgutil
/
quant.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-08-01
|
9KB
|
453 lines
/*
* Copyright 1991, 1992, 1993, 1994, Silicon Graphics, Inc.
* All Rights Reserved.
*
* This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, Inc.;
* the contents of this file may not be disclosed to third parties, copied or
* duplicated in any form, in whole or in part, without the prior written
* permission of Silicon Graphics, Inc.
*
* RESTRICTED RIGHTS LEGEND:
* Use, duplication or disclosure by the Government is subject to restrictions
* as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data
* and Computer Software clause at DFARS 252.227-7013, and/or in similar or
* successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished -
* rights reserved under the Copyright Laws of the United States.
*/
/*
* quant -
* general median cut algorithm follows. This is
* based on heckbert's median cut algoirthm.
*
* Paul Haeberli - 1990
*/
#include "stdio.h"
#include "quant.h"
#define MAXCOLORS (256)
#define NINCHUNK (40)
#define SPACEDIM (64)
#define NOCOLOR (10000)
#define RDIR (1)
#define GDIR (2)
#define BDIR (3)
typedef struct colorvol {
struct colorvol *next;
long rmin, rmax, rcut, rdev;
long gmin, gmax, gcut, gdev;
long bmin, bmax, bcut, bdev;
int avgdev;
int npixels;
int splitdir;
int number;
} colorvol;
static long carray[SPACEDIM][SPACEDIM][SPACEDIM];
static short iarray[SPACEDIM][SPACEDIM][SPACEDIM];
static long ctab[MAXCOLORS][3];
static short rbuf[MAXCOLORS];
static short gbuf[MAXCOLORS];
static short bbuf[MAXCOLORS];
static colorvol *cvarray[MAXCOLORS];
static colorvol *fvols = 0;
static colorvol *root = 0;
static int ncolors;
static int nnodes;
static int heckmode;
static clearspace()
{
long i;
long *lptr;
lptr = carray[0][0];
for(i=SPACEDIM*SPACEDIM*SPACEDIM; i--;)
*lptr++ = 0;
}
static navgtospace(r, g, b)
int r, g, b;
{
int index, n;
index = iarray[r][g][b];
if(index == NOCOLOR)
return;
n = carray[r][g][b];
if(n) {
r = (255*r)/(SPACEDIM-1);
g = (255*g)/(SPACEDIM-1);
b = (255*b)/(SPACEDIM-1);
ctab[index][0] += n*r;
ctab[index][1] += n*g;
ctab[index][2] += n*b;
}
}
static freevol( cv )
colorvol *cv;
{
if( cv ) {
cv->next = fvols;
fvols = cv;
}
}
static colorvol *newvol()
{
colorvol *cv;
int i;
if(!fvols) {
cv = (colorvol *)mymalloc(NINCHUNK*sizeof(colorvol));
for(i=0; i<NINCHUNK; i++)
freevol(cv++);
}
cv = fvols;
fvols = fvols->next;
return cv;
}
static zaphist(hist,min,max)
long hist[];
long min, max;
{
int i;
for(i=min; i<=max; i++)
hist[i] = 0;
}
static rangehist(hist,min,max,npix,ravg,rdev)
long hist[];
long min, max, npix, *ravg, *rdev;
{
int i;
long delta, avg, dev;
avg = 0;
for(i=min; i<=max; i++) {
avg += i*hist[i];
}
avg = avg/npix;
dev = 0;
for(i=min; i<=max; i++) {
delta = i-avg;
dev += hist[i]*delta*delta;
}
*ravg = avg;
*rdev = dev>>8;
if(avg == min)
return 0;
else
return 1;
}
#define FLAGMAX (12000)
static makenode(rmin,rmax,gmin,gmax,bmin,bmax)
int rmin, rmax, gmin, gmax, bmin, bmax;
{
colorvol *cv, *ptr;
int Rmin, Rmax, Gmin, Gmax, Bmin, Bmax;
int r, g, b;
int rok, gok, bok;
long npixels, val;
long rhist[SPACEDIM];
long ghist[SPACEDIM];
long bhist[SPACEDIM];
cv = newvol();
Rmin = Gmin = Bmin = FLAGMAX;
Rmax = Gmax = Bmax = -FLAGMAX;
zaphist(rhist,rmin,rmax);
zaphist(ghist,gmin,gmax);
zaphist(bhist,bmin,bmax);
npixels = 0;
for(b=bmin; b<=bmax; b++) {
for(g=gmin; g<=gmax; g++) {
for(r=rmin; r<=rmax; r++) {
val = carray[r][g][b];
if(val) {
if(r<Rmin)
Rmin = r;
if(g<Gmin)
Gmin = g;
if(b<Bmin)
Bmin = b;
if(r>Rmax)
Rmax = r;
if(g>Gmax)
Gmax = g;
if(b>Bmax)
Bmax = b;
npixels+=val;
rhist[r] += val;
ghist[g] += val;
bhist[b] += val;
}
}
}
}
if(npixels == 0)
return;
cv->npixels = npixels;
cv->rmin = Rmin;
cv->gmin = Gmin;
cv->bmin = Bmin;
cv->rmax = Rmax;
cv->gmax = Gmax;
cv->bmax = Bmax;
rok = rangehist(rhist,cv->rmin,cv->rmax,npixels,&cv->rcut,&cv->rdev);
gok = rangehist(ghist,cv->gmin,cv->gmax,npixels,&cv->gcut,&cv->gdev);
bok = rangehist(bhist,cv->bmin,cv->bmax,npixels,&cv->bcut,&cv->bdev);
if(rok && cv->rdev>=cv->gdev && cv->rdev>=cv->bdev)
cv->splitdir = RDIR;
else if(gok && cv->gdev>=cv->rdev && cv->gdev>=cv->bdev)
cv->splitdir = GDIR;
else if(bok)
cv->splitdir = BDIR;
else
cv->splitdir = 0;
cv->avgdev = cv->rdev+cv->gdev+cv->bdev;
#ifdef OLDWAY
cv->avgdev = ILUM(cv->rdev,cv->gdev,cv->bdev);
#endif
cv->next = root;
root = cv;
nnodes++;
}
static avgtospace(short *r, short *g, short *b, int n)
{
int tr, tg, tb;
int ir, ig, ib;
int index;
while(n--) {
tr = *r++;
tg = *g++;
tb = *b++;
ir = (SPACEDIM*tr)/256;
ig = (SPACEDIM*tg)/256;
ib = (SPACEDIM*tb)/256;
index = iarray[ir][ig][ib];
if(index == NOCOLOR) {
fprintf(stderr,"hquant: bad poop\n");
exit(1);
} else {
ctab[index][0] += tr;
ctab[index][1] += tg;
ctab[index][2] += tb;
}
}
}
static divspace(cv)
colorvol *cv;
{
switch(cv->splitdir) {
case RDIR:
makenode(cv->rmin,cv->rcut-1,cv->gmin,cv->gmax,cv->bmin,cv->bmax);
makenode(cv->rcut,cv->rmax, cv->gmin,cv->gmax,cv->bmin,cv->bmax);
break;
case GDIR:
makenode(cv->rmin,cv->rmax,cv->gmin,cv->gcut-1,cv->bmin,cv->bmax);
makenode(cv->rmin,cv->rmax,cv->gcut,cv->gmax, cv->bmin,cv->bmax);
break;
case BDIR:
makenode(cv->rmin,cv->rmax,cv->gmin,cv->gmax,cv->bmin,cv->bcut-1);
makenode(cv->rmin,cv->rmax,cv->gmin,cv->gmax,cv->bcut,cv->bmax );
break;
default:
fprintf(stderr,"quant: divspace: bad poop %d\n",cv->splitdir);
exit(1);
}
nnodes--;
cv->npixels = 0;
cv->avgdev = 0;
}
/*
* gammawarp stuff follows
*
*
*/
static float curgamma;
static short gamtab[256];
static gammawarp(sbuf,gam,n)
short *sbuf;
float gam;
int n;
{
int i;
float f;
if(gam!=curgamma) {
for(i=0; i<256; i++)
gamtab[i] = 255*pow(i/255.0,gam)+0.5;
curgamma = gam;
}
while(n--) {
*sbuf = gamtab[*sbuf];
sbuf++;
}
}
static lumwarp(r,g,b,gamma)
short *r, *g, *b;
float gamma;
{
int max, nmax;
max = *r;
if(*g>max)
max = *g;
if(*b>max)
max = *b;
if(max>0) {
nmax = 255*pow(max/255.0,gamma)+0.49;
*r = (nmax * *r)/max;
*g = (nmax * *g)/max;
*b = (nmax * *b)/max;
} else {
*r = 0;
*g = 0;
*b = 0;
}
}
void optmapbegin(int nc, int mode)
{
ncolors = nc;
if(ncolors>MAXCOLORS) {
fprintf(stderr,"quant: num colors exceeds %d\n",MAXCOLORS);
exit(1);
}
heckmode = mode;
clearspace();
}
void optmapadd( short *r, short *g, short *b, int n)
{
int ir, ig, ib;
while(n--) {
ir = (SPACEDIM*(*r++))/256;
ig = (SPACEDIM*(*g++))/256;
ib = (SPACEDIM*(*b++))/256;
carray[ir][ig][ib]++;
}
}
cmap *optmapend( void )
{
int i, r, g, b;
int totcolors, bigest;
colorvol *bigcv, *cv;
/* repeat division of the smallest piece for ncolors */
root = NULL;
nnodes = 0;
makenode(0,SPACEDIM-1,0,SPACEDIM-1,0,SPACEDIM-1);
if(heckmode) {
while(nnodes<ncolors) {
bigcv = 0;
bigest = 0;
cv = root;
while(cv) {
if(cv->splitdir && cv->npixels>bigest) {
bigcv = cv;
bigest = cv->npixels;
}
cv = cv->next;
}
if(bigcv)
divspace(bigcv);
else {
fprintf(stderr,"N colors reduced to %d from %d\n",nnodes,ncolors);
ncolors = nnodes;
break;
}
}
} else {
while(nnodes<ncolors) {
bigcv = 0;
bigest = 0;
cv = root;
while(cv) {
if(cv->splitdir && cv->avgdev>bigest) {
bigcv = cv;
bigest = cv->avgdev;
}
cv = cv->next;
}
if(bigcv)
divspace(bigcv);
else {
fprintf(stderr,"Ncolor reduced to %d from %d\n",nnodes,ncolors);
ncolors = nnodes;
break;
}
}
}
/* clear the iarray to NOCOLOR */
for(r=0; r<SPACEDIM; r++)
for(g=0; g<SPACEDIM; g++)
for(b=0; b<SPACEDIM; b++)
iarray[r][g][b] = NOCOLOR;
/* set the color numbers for all the volumes */
cv=root;
totcolors = 0;
while(cv) {
if(cv->npixels) {
cvarray[totcolors] = cv;
cv->number = totcolors;
for(r=cv->rmin; r<=cv->rmax; r++)
for(g=cv->gmin; g<=cv->gmax; g++)
for(b=cv->bmin; b<=cv->bmax; b++)
iarray[r][g][b] = totcolors;
totcolors++;
}
cv = cv->next;
}
/* read the input image again, finding average colors */
for(i=0; i<ncolors; i++) {
ctab[i][0] = 0;
ctab[i][1] = 0;
ctab[i][2] = 0;
}
for(b=0; b<SPACEDIM; b++) {
for(g=0; g<SPACEDIM; g++) {
for(r=0; r<SPACEDIM; r++) {
navgtospace(r,g,b);
}
}
}
for(i=0; i<ncolors; i++) {
ctab[i][0] /= cvarray[i]->npixels;
ctab[i][1] /= cvarray[i]->npixels;
ctab[i][2] /= cvarray[i]->npixels;
rbuf[i] = ctab[i][0];
gbuf[i] = ctab[i][1];
bbuf[i] = ctab[i][2];
}
return newcmap(rbuf,gbuf,bbuf,0,ncolors);
}